#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <log.h>
#include <helper_unit.h>
#include <net.h>
#include <memcheck.h>
#include <http_decoder.h>
#include <http_decoder.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <sys/types.h>
#include <iomanip>
#include <fstream>
#include <ctime>
#include <iostream>
#include "dll.h"

#include "socket.h"

using namespace comm::sockcommu;

//#define FIFO "/tmp/my_fifo"

HTTP_SVR_NS_BEGIN

/*This macro is used to prevent buffer from overflowing .
 *the lenght of buffer is MAX,and it's frist INI byte is used,
 *and we will append INC bytes, if the buffer will overflow,
 *log the error and return -1. by allan
 */

void HexDumpImp(const void *pdata, unsigned int len, int num)
{
       if(pdata == 0 || len == 0)
        {
                return;
        }
    
        int cnt = 0;
        int n = 0;
        int cnt2 = 0;
        const char *data = (const char *)pdata;
        cout<<"Address               Hexadecimal values                  Printable\n";
        cout<<"-------  -----------------------------------------------  -------------\n";
        cout<<"num:[" << num << "]";
        cout << "\n";
        unsigned char buffer[20];
        unsigned int rpos = 0;

        while ( 1 )
        {
                if(len <= rpos)
                {
                        break;
                }
                if(len >= rpos + 16)
                {
                        memcpy(buffer, data + rpos, 16);
                        rpos += 16;
                        cnt = 16;
                }
                else
                {
                        memcpy(buffer, data + rpos, len - rpos);
                        cnt = len - rpos;
                        rpos = len;
                }
                if(cnt <= 0)
                {
                        return;
                }

                 cout << setw(7) << ( int ) rpos << "  ";

                cnt2 = 0;
                for ( n = 0; n < 16; n++ )
                {
                        cnt2 = cnt2 + 1;
                        if ( cnt2 <= cnt )
                        {
                                cout << hex << setw(2) << setfill ( '0' ) <<  (unsigned int)buffer[n];
                        }
                        else
                        {
                                cout << "  ";
                        }
                        cout << " ";
                }

                cout << setfill ( ' ' );

                cout << " ";
                cnt2 = 0;
                for ( n = 0; n < 16; n++ )
                {
                        cnt2 = cnt2 + 1;
                        if ( cnt2 <= cnt )
                        {
                                if ( buffer[n] < 32 || 126 < buffer[n] )
                                {
                                        cout << '.';
                                }
                                else
                                {
                                        cout << buffer[n];
                                }
                        }
                }
                cout << "\n";
                cout << dec;
        }

        return;
}


#define SAFE_QUIT(MAX,INI,INC) do {\
		if((unsigned)(INI)+(unsigned)(INC) >=(unsigned)(MAX)){\
				log_error("the head is too big");\
				return -1;\
		}\
}while(0);



class CClientUnit; 


CHelperUnit::CHelperUnit(CDecoderUnit* decoderunit, THttpStateInfo* httpinfo) :
		CPollerObject (decoderunit->pollerunit()),
		_stage (CONN_IDLE),
		_owner (decoderunit),
		_httpinfo (httpinfo),
		_decoder (CHttpDecoder::Instance()),
		_lastCheckPoint (0),
		_compress_inqueue_flag(0),
		_terminate_byforce_flag(0),
        _rspBuffer(NULL),
        _rspLen(0),
        _r(*_helpMp),
		_w(*_helpMp)
{

}

CHelperUnit::~CHelperUnit ()
{
		FREE_CLEAR(_rspBuffer);
}

int CHelperUnit::responseCmd(const char *pBuf , int bufsize)
{
    if( pBuf == NULL )
    {
        return -1;
    }

    if ((_rspBuffer = (char*)REALLOC(_rspBuffer, bufsize+1)) == NULL)
	{
			log_error("realloc memory failed,netfd[%d]", netfd);
			return -1;
	}
	
	memcpy(_rspBuffer, pBuf, bufsize);
	_rspLen = bufsize;
	return _owner->cgi_input();
}


int CHelperUnit::send (void)
{
		if (NULL == _decoder)
		{
				log_error ("get CHttpDecoder instance failed.");

				return -1;
		}

		EnableInput ();

		if (connect() < 0)
		{
			return -1;
		}
	
		
		//HexDumpImp(_httpinfo->_allsendbuf, _httpinfo->_allsendlen, 1);

		return this->send_to_cgi();
}


int CHelperUnit::get_helper_rspBuff(char ** rspBuff, int & rspLen)
{
	*rspBuff = _rspBuffer;
	rspLen = _rspLen;
	return 0;
}

int CHelperUnit::recv_from_cgi(void)
{
		int ret = ::recv (netfd, _curRecvBuf, MAX_HELPER_RECV_BUF, 0);
		
		log_debug("*STEP: unix fd[%d] recv len[%d] this time _lastCheckPoint[%d]", netfd, ret, _lastCheckPoint);
		if (-1 == ret)
		{
				
				if((errno==EINTR) || (errno==EAGAIN) || (errno==EINPROGRESS))
				{
						_stage = CONN_DATA_RECVING;
						return 0;
				}
				
				_stage = CONN_FATAL_ERROR;
				log_error ("*STEP: recv from failed, unix fd[%d], checkpoint[%d], msg[%m]", netfd,_lastCheckPoint);

				return -1;
		}

		if (0 == ret)
		{
				_stage = CONN_DISCONNECT;
				return -1;
		}
		
		_r.append(_curRecvBuf, ret);
		
		//HexDumpImp(_r.data(), _r.data_len(), 31);

        _stage = CONN_FATAL_ERROR;
        return ret;
}

void CHelperUnit::flush_cgi_result()
{
		_owner->cgi_input();
		_owner->complete();
}

int CHelperUnit::connect (void)
{
		int connect_errno = -1;
		int ret = -1;

		log_debug("*STEP: PROXY CNet::unix_connect before");


		if (_stage == CONN_IDLE)
		{
			ret = CNet::tcp_connect(&netfd, TGlobal::_server_ip.c_str(), TGlobal::_server_port, 0);
		}
		else
		{
			if (netfd < 0)
			{
				ret = CNet::tcp_connect(&netfd, TGlobal::_server_ip.c_str(), TGlobal::_server_port, 0);
			}
			else
			{
				ret = 0;
			}
		}

		log_debug("*STEP: PROXY CNet::unix_connect ret :[%d], netfd:[%d]", ret, netfd);

		if (ret < 0)
		{
				if (ret == SOCKET_CREATE_FAILED)
				{
						log_error("helper create socket failed, errno[%d], msg[%s]", connect_errno, strerror(connect_errno));
						return -1;
				}

				if(connect_errno != EINPROGRESS)
				{
						log_error("*STEP: PROXY connect to failed, errno[%d], msg[%s]",
										connect_errno , strerror(connect_errno));
						return -1;
				}

				_stage = CONN_CONNECTING;
				goto exit;
		}

		_stage = CONN_CONNECTED;

exit:
		return AttachPoller();
}

int CHelperUnit::send_to_cgi(void)
{
		char*   sendbuf = _httpinfo->_allsendbuf;
		int     sendlen = _httpinfo->_allsendlen;
		
		//HexDumpImp(_httpinfo->_allsendbuf, _httpinfo->_allsendlen, 2);

		if (_w.data_len() != 0)
		{
			int ret = ::send (netfd, _w.data(), _w.data_len(), 0);
			
			if(-1 == ret)
			{
				if(errno == EINTR || errno == EAGAIN || errno == EINPROGRESS)
				{
					EnableOutput ();
					ApplyEvents ();
					
					_w.append(sendbuf, sendlen);

					_httpinfo->free_web_buf ();
					return CONN_DATA_SENDING;
				}
		
				
				DisableInput ();
				DisableOutput ();
				_stage = CONN_FATAL_ERROR;
				return -1;
			}

			if((unsigned)ret == _w.data_len())
			{
				_w.skip(ret);				
			}
			else if ((unsigned)ret < _w.data_len())
			{
				EnableOutput ();
				ApplyEvents ();
				
				_w.skip(ret);
				_w.append(sendbuf, sendlen);
				
				_httpinfo->free_web_buf ();
				_stage = CONN_DATA_SENDING;
				return ret;
			}
			
		}
			
		int ret = ::send (netfd, sendbuf, sendlen, 0);

		log_debug("*STEP: sending package to CGIHOST, unix fd[%d], total len[%d], total sent[%d], buffer[%s]",
			netfd, sendlen, ret, sendbuf);

		if(-1 == ret)
		{
				if(errno == EINTR || errno == EAGAIN || errno == EINPROGRESS)
				{
						EnableOutput ();
						ApplyEvents ();
						_w.append(sendbuf, sendlen);
						_httpinfo->free_web_buf ();
						return CONN_DATA_SENDING;
				}

				log_error ("*STEP: sending package to failed, ret code[%d], msg[%m]",  ret);
                
				DisableInput ();
				DisableOutput ();
				
				_stage = CONN_FATAL_ERROR;

				return -1;
		}

		if(ret == sendlen)
		{
				DisableOutput ();
				_stage = CONN_SEND_DONE;
				_httpinfo->free_web_buf ();
				return ret;
		}

		if (ret < sendlen)
		{
				EnableOutput ();
				ApplyEvents ();
				_w.append(sendbuf+ret , sendlen-ret);
				_httpinfo->free_web_buf ();
				_stage = CONN_DATA_SENDING;

				return ret;
		}
		DisableInput ();
		DisableOutput ();
		_stage = CONN_FATAL_ERROR;

		return -1;
}

int CHelperUnit::InputNotify (void)
{
		int ret             = 0;//this->recv ();

		ret = recv_from_cgi();

		if (ret < 0)
		{
				log_error("call recv failed, helper client netfd[%d]", netfd);
				_owner->complete ();

				return POLLER_COMPLETE;
		}

		if (CONN_RECV_DONE == _stage)
		{
				_owner->cgi_input ();

				return POLLER_COMPLETE;
		}

		return POLLER_SUCC;
}

int CHelperUnit::OutputNotify (void)
{
		return _owner->cgi_output ();
}

int CHelperUnit::HangupNotify (void)
{
		recv_from_cgi();

		switch (_stage)
		{
				case CONN_DATA_RECVING:
						return POLLER_SUCC;

				case CONN_RECV_DONE:
					
					flush_cgi_result();
						return POLLER_COMPLETE;

				default:
						break;
		}

		_owner->cgi_hangup();
		return POLLER_COMPLETE;
}



HTTP_SVR_NS_END

